﻿using Away.EventBusCore.Commands;
using Away.EventBusCore.Commands.Impl;
using Away.EventBusCore.DomainEvents;
using Away.EventBusCore.DomainEvents.Impl;
using System.Reflection;

namespace Away.EventBusCore;

public static class ServiceCollectionExtensions
{
    public static IServiceCollection AddMediatR(this IServiceCollection services)
    {
        services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblies(AppDomain.CurrentDomain.GetAssemblies()));
        return services;
    }

    /// <summary>
    /// 添加事件总线
    /// </summary>
    /// <param name="services"></param>
    /// <param name="action"></param>
    /// <returns></returns>
    public static IServiceCollection AddDomainEvent(this IServiceCollection services, Action<DomainEventOptionsBuilder> action)
    {
        DomainEventOptionsBuilder options = new();
        var assemblies = AppDomain.CurrentDomain.GetAssemblies();
        var types = assemblies.SelectMany(o => o.DefinedTypes.Where(t => t.GetCustomAttribute<DeAttribute>() != null))
              .ToDictionary(key => (Activator.CreateInstance(key.AsType()) as IDe)!.EventId, val => val.AsType());

        foreach (var (k, v) in types)
        {
            options.EventBusTypes.TryAdd(k, v);
        }
        action(options);
        services.Configure<DomainEventOptionsBuilder>(o =>
        {
            foreach (var (k, v) in options.EventBusTypes)
            {
                o.EventBusTypes.TryAdd(k, v);
            }
            o.ConnectionString = options.ConnectionString;
            o.ExchangeName = options.ExchangeName;
            o.HostType = options.HostType;
        });

        if (options.HostType == DomainEventHostType.Local)
        {
            services.AddSingleton<IDomainEventPublisher, LocalDomainEventPublisher>();
        }
        else if (options.HostType == DomainEventHostType.Remote)
        {
            services.AddSingleton<IDomainEventPublisher, RemoteDomainEventPublisher>();
            services.AddSingleton<IDomainEventHost, RemoteDomainEventPublisher>();
            services.AddHostedService<DomainEventHostedService>();
        }
        return services;
    }

    /// <summary>
    /// 添加命令事件
    /// </summary>
    /// <param name="services"></param>
    /// <param name="action"></param>
    /// <returns></returns>
    public static IServiceCollection AddCommand(this IServiceCollection services, Action<CommandOptionsBuilder> action)
    {
        CommandOptionsBuilder options = new();
        var assemblies = AppDomain.CurrentDomain.GetAssemblies();
        var types = assemblies.SelectMany(o => o.DefinedTypes.Where(t => t.GetCustomAttribute<CmdAttribute>() != null))
              .ToDictionary(key => (Activator.CreateInstance(key.AsType()) as ICmd)!.EventId, val => val.AsType());

        foreach (var (k, v) in types)
        {
            options.EventBusTypes.TryAdd(k, v);
        }
        action(options);
        services.Configure<CommandOptionsBuilder>(o =>
        {
            foreach (var (k, v) in options.EventBusTypes)
            {
                o.EventBusTypes.TryAdd(k, v);
            }
            o.Host = options.Host;
            o.User = options.User;
            o.Password = options.Password;
            o.HostType = options.HostType;
        });

        if (options.HostType == CommandHostType.Local)
        {
            services.AddSingleton<ICommandSender, LocalCommandSender>();
        }
        else if (options.HostType == CommandHostType.Remote)
        {
            services.AddSingleton<ICommandSender, RemoteCommandSender>();
            services.AddSingleton<ICommandHost, RemoteCommandSender>();
            services.AddHostedService<CommandHostedService>();
        }

        return services;
    }
}
